﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Threading;
using YKXiangQi.EventHandlers;
using YKXiangQi.Models;

namespace YKXiangQi.Adapters
{
    public abstract class EngineAdapter
    {
        public event EngineOutputReceivedEventHandler EngineOuputReceived;

        protected static readonly object obj = new object();
        protected ManualResetEvent _manualResetEvent = new ManualResetEvent(false);
        protected Process _process;

        public EngineSettings Settings { get; set; }

        protected bool IsLastCommandStopThinking { get; set; }

        protected bool IsProcessAlive
        {
            get { return _process != null && !_process.HasExited; }
        }

        public bool IsStarted
        {
            get { return IsProcessAlive; }
        }

        protected void SendCommand(string command)
        {
            if (IsProcessAlive)
            {
                _process.StandardInput.WriteLine(command);
                _process.StandardInput.Flush();
            }
        }

        protected void SendCommand(string command, params object[] parameters)
        {
            var commandWithParams = string.Format(command, parameters);
            SendCommand(commandWithParams);
        }

        protected virtual void OnEngineOutputReceived(EngineOutputReceivedEventArgs e)
        {
            if (EngineOuputReceived != null)
                EngineOuputReceived(this, e);
        }

        public bool Start()
        {
            return Settings != null && StartEngine();
        }

        private bool StartEngine()
        {
            try
            {
                _process = new Process
                {
                    StartInfo =
                    {
                        UseShellExecute = false,
                        RedirectStandardOutput = true,
                        RedirectStandardInput = true,
                        CreateNoWindow = true,
                        WindowStyle = ProcessWindowStyle.Hidden,
                        FileName = Settings.Path,
                        WorkingDirectory = Path.GetDirectoryName(Settings.Path)
                    }
                };
                _process.OutputDataReceived += OnOutputDataReceived;

                if (_process.Start())
                {
                    _process.BeginOutputReadLine();
                    InitEngine();
                }
            }
            catch (Exception ex)
            {
                //log exception
                if (IsProcessAlive)
                    _process.Kill();
                return false;
            }

            return true;
        }

        public void Exit()
        {
            if (IsProcessAlive)
            {
                _process.Kill();
            }
        }

        public abstract void Stop();
        
        public abstract void InitEngine();

        public abstract void NewGame();

        public abstract void SetPosition(string fen);

        public abstract void Go();

        public abstract void Go(TimerSettings timerSettings);

        public abstract void Analyze();

        public abstract void StopThinking();

        protected abstract void OnOutputDataReceived(object sender, DataReceivedEventArgs e);
    }
}
